import keyword
import os

from PyQt6.Qsci import (QsciAPIs, QsciLexerCPP, QsciLexerCSharp, QsciLexerCSS, 
                        QsciLexerCustom, QsciLexerHTML, QsciLexerJava,
                        QsciLexerJavaScript, QsciLexerJSON, QsciLexerMarkdown,
                        QsciLexerPython, QsciScintilla,QsciLexerBash,QsciLexerXML)
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QFont, QTextDocument
from PyQt6.QtWidgets import (QFileDialog, QSplitter, QTabWidget, QVBoxLayout,
                             QWidget)

from ...config import R
from .calltips import PYTHON3_APIS, SHARED_APIS


class Editor(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.tabs = TabPane(self)
        self.splitter = QSplitter(Qt.Orientation.Vertical)
        self.splitter.addWidget(self.tabs)
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.splitter)
        self.setLayout(self.layout)
        self.filename = ""
        self.document = QTextDocument()

    def add_pane(self, pane):
        self.splitter.addWidget(pane)

    def add_tab(self, path, text):
        ed = CodeEdit(path, text)
        i = self.tabs.addTab(ed, ed.get_label())
        ed.modificationChanged.connect(lambda: self.mark_tab_modified(i))
        self.tabs.setCurrentIndex(i)

    def mark_tab_modified(self, index):
        index = self.tabs.currentIndex()
        ed = self.tabs.currentWidget()
        self.tabs.setTabText(index, ed.get_label())

    def zoom_in(self):
        for tab in self.tabs:
            if hasattr(tab, "zoomIn"):
                tab.zoomIn(2)

    def zoom_out(self):
        for tab in self.tabs:
            if hasattr(tab, "zoomOut"):
                tab.zoomOut(2)

    def new(self):
        self.add_tab(None, "")

    def open(self):
        filename, _ = QFileDialog.getOpenFileName(
            self,
            "Open File",
            os.path.dirname(self.filename),
            "Python File (*.py) ;; Markdown File (*.md) ;; CSharp File (*.cs) ;; CPP File (*.cpp, *.h, *c) ;; All Files (*)",
        )

        try:
            with open(filename, encoding=R.Encoding) as f:
                data = f.read()
        except FileNotFoundError:
            pass
        else:
            self.add_tab(filename, data)
            self.filename = filename

    def save(self):
        ed = self.tabs.currentWidget()
        if ed is None or not ed.isModified():
            return
        if ed.path is None:
            path, _ = QFileDialog.getSaveFileName(
                self,
                "Save File",
                os.path.dirname(self.filename),
                "Python File(*.py) ;; Markdown File (*.md) ;; CSharp File (*.cs) ;; CPP File (*.cpp, *.h, *c) ;; All Files (*)",
            )
            name, ext = os.path.splitext(path)
            if not ext and _ == "Python File(*.py)":
                ed.path = name + ".py"
            else:
                ed.path = path
        if ed.path:
            with open(ed.path, "w", encoding=R.Encoding) as f:
                f.write(ed.text())
            ed.setModified(False)


class TabPane(QTabWidget):
    def __init__(self, parent):
        super().__init__(parent)
        self.setTabsClosable(True)
        self.parent = parent
        self.tabCloseRequested.connect(self.closeTab)

    def closeTab(self, i):
        self.parent.save()
        self.removeTab(i)

    def __len__(self):
        return self.count()

    def __getitem__(self, index):
        t = self.widget(index)
        if not t:
            raise IndexError(index)
        return t


class CodeEdit(QsciScintilla):
    def __init__(self, path, text):
        super().__init__()
        self.path = path
        self.setText(text)
        self.setModified(False)

        self.apis = PYTHON3_APIS + SHARED_APIS
        self.config()

    def config(self):
        font = QFont(R.FontName)
        font.setFixedPitch(True)
        font.setPointSize(R.FontSize)
        self.setFont(font)

        self.setIndentationsUseTabs(False)
        self.setIndentationWidth(4)
        self.setTabWidth(4)
        self.setMarginLineNumbers(0, True)
        self.setMarginWidth(0, 30)
        self.setBraceMatching(QsciScintilla.BraceMatch.StrictBraceMatch)
        self.lexer = TextLexer()
        if self.path:
            if self.path.endswith(".py"):
                self.lexer = self.python_lexer()
            elif self.path.endswith(".cs"):
                self.lexer = QsciLexerCSharp()
            elif self.path.endswith((".cpp", ".c", ".h")):
                self.lexer = QsciLexerCPP()
            elif self.path.endswith('.java'):
                self.lexer = QsciLexerJava()
            elif self.path.endswith('.js'):
                self.lexer = QsciLexerJavaScript()
            elif self.path.endswith('.css'):
                self.lexer = QsciLexerCSS()
            elif self.path.endswith(('.html', '.htm')):
                self.lexer = QsciLexerHTML()
            elif self.path.endswith('.sh'):
                self.lexer = QsciLexerBash()
            elif self.path.endswith('.xml'):
                self.lexer = QsciLexerXML()
            elif self.path.endswith('.josn'):
                self.lexer = QsciLexerJSON()
            elif self.path.endswith(".md"):
                self.lexer = QsciLexerMarkdown()

        self.lexer.setFont(font)
        self.setLexer(self.lexer)

        api = QsciAPIs(self.lexer)
        for a in self.apis:
            api.add(a)
        api.prepare()

        self.setAutoCompletionCaseSensitivity(False)
        self.setAutoCompletionThreshold(2)
        self.setAutoCompletionSource(QsciScintilla.AutoCompletionSource.AcsAll)
        self.setAutoCompletionReplaceWord(True)

        self.setFolding(QsciScintilla.FoldStyle.BoxedTreeFoldStyle)

        self.setAutoIndent(True)
        self.setUtf8(True)

    def python_lexer(self):
        lex = PythonLexer()
        return lex

    def get_label(self):
        if self.path:
            label = os.path.basename(self.path)
        else:
            label = "untitled"

        if self.isModified():
            return label + " *"
        else:
            return label


class PythonLexer(QsciLexerPython):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setHighlightSubidentifiers(False)

    def keywords(self, flag: int):
        if flag == 1:
            keys = keyword.kwlist + ["self", "cls"]
        else:
            return None
        return " ".join(keys)


class MarkdownLexcel(QsciLexerMarkdown):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def keywords(self, set: int) -> str:
        return super().keywords(set)


class TextLexer(QsciLexerCustom):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
